package com.wan.thread.actor;

import cn.hutool.log.StaticLog;
import com.wan.core.shutdown.ShutdownManager;

import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * actor线程池
 * 内置默认全局自增的id，使每个任务能够均衡的分配到各个线程上
 * 同时可以指定id来模除绑定到指定线程上
 * 例如：玩家登录时获取绑定唯一序列号->通过AUTO_ID,下线前全部操作通过这个序列号绑定到线程上
 */
public class ActorExecutor implements Executor {
    private final AtomicLong autoId;
    private final ActorThread[] actorThreads;
    private final String groupName;

    public ActorExecutor(int threadSize,String groupName){
        if(threadSize <= 0){
            throw new RuntimeException("actor线程数量定义必须大于0");
        }
        this.groupName = groupName;
        actorThreads = new ActorThread[threadSize];
        for (int i = 1; i <= threadSize; i++) {
            String name = groupName + "-" + i;
            ActorThread actorThread = new ActorThread(name);
            actorThreads[i-1] = actorThread;
        }
        this.autoId = new AtomicLong(0);
        ShutdownManager.addShutdown(groupName,this::shutdown);
    }

    public ActorExecutor(String groupName){
        this(Math.max(Runtime.getRuntime().availableProcessors(),4),groupName);
    }

    public long autoId(){
        return autoId.incrementAndGet();
    }

    public void execute(Actor actor){
        long exceptId = actor.asyncId();
        int index = (int) Math.abs(exceptId % actorThreads.length);
        actorThreads[index].addActor(actor);
    }

    @Override
    public void execute(Runnable command) {
        if(command instanceof Actor){
            Actor actor = (Actor) command;
            execute(actor);
        }
        long exceptId = autoId();
        int index = (int) Math.abs(exceptId % actorThreads.length);
        actorThreads[index].addActor(command);
    }

    public void shutdown(){
        int success = 0;
        int fail = 0;
        for (ActorThread actorThread : this.actorThreads) {
            actorThread.shutdown();
            success += actorThread.getSuccessTotal();
            fail += actorThread.getFailTotal();
        }
        StaticLog.info("shutdown -> actor-{}[执行任务成功数量：{}，失败数量：{}]",groupName,success,fail);
    }
}
